package middleware

import (
	"github.com/gin-gonic/gin"
	"github.com/gobwas/glob"
	"one-wiki/commons"
	"one-wiki/database"
	"strings"
)

const (
	// ANON 允许匿名访问
	ANON = "anon"
	// AUTH 需要认证
	AUTH = "auth"
	// 需要指定角色（用户组）
)

var ApiRules []*commons.ApiRules

// AuthenticationMiddleware 权限认证中间件
func AuthenticationMiddleware() gin.HandlerFunc {
	return Authentication
}

// Authentication 认证及鉴权中间件()
func Authentication(context *gin.Context) {
	// 请求Uri
	uri := context.Request.RequestURI

	// 文件预览
	if strings.HasPrefix(uri, "/file/preview") {
		// 继续链路
		context.Next()
	}

	// 请求方法
	method := context.Request.Method

	// token
	token := context.Request.Header.Get("Authorization")

	// 校验token判断是否登录 并暂存localSession
	isLogin := checkTokenAndCacheSession(token, context)

	// 如果有token但校验为未登录，认为登录过期
	if token != "" && !isLogin {
		panic(commons.LoginExpiredErr)
	}

	// 根据uri和method 查询该接口需要的角色（即：用户组）
	hasAnyGroup := checkGroupForApi(uri, method, context)

	// 如果有需要的角色
	if !hasAnyGroup {
		if isLogin {
			panic(commons.UnAuthorizationErr)
		} else {
			panic(commons.UnAuthenticationErr)
		}
	}

	// 继续链路
	context.Next()
}

func checkGroupForApi(uri string, method string, context *gin.Context) bool {
	// 获取localSession
	value, exists := context.Get(commons.KEY_SESSION)
	if !exists {
		return false
	}
	localSession, ok := value.(commons.Session)
	if !ok {
		return false
	}

	// 获取api列表
	if ApiRules == nil || len(ApiRules) == 0 {
		loadApiRules()
	}

	// 有权访问该该api的角色（用户组）列表
	var requiredGroups *[]int64

	// 当前会话中拥有的角色
	var affiliatedGroups = localSession.Groups

	// 匹配规则
	for _, rule := range ApiRules {
		// 定义一个通配符路径模式
		pattern := rule.UriPattern

		// 实例化一个 Glob 对象
		g := glob.MustCompile(pattern)

		// 调用 Match 方法进行匹配
		matched := g.Match(uri)

		if matched {
			requiredGroups = &rule.Groups
			break
		}
	}

	// 比对
	for _, rg := range *requiredGroups {
		for _, ag := range affiliatedGroups {
			if rg == ag {
				return true
			}
		}
	}
	return false
}

func loadApiRules() {
	// 查询所有api资源
	apis, err := database.GetAllApis()
	if err != nil {
		panic(commons.CustomServerError("api访问规则异常"))
	}

	// 查询所有api权限
	groupPermissions, err := database.ListGroupPermissionsByResourceType(commons.RESOURCE_API)
	if err != nil {
		panic(commons.CustomServerError("api访问规则异常"))
	}

	// 遍历apis和groupPermissions
	for _, api := range apis {
		var groups []int64
		// 查找当前API对应的所有组权限
		for _, gp := range groupPermissions {
			if gp.ResourceID == api.ID {
				// 将组权限中的组ID添加到groups列表中
				groups = append(groups, gp.GroupID)
			}
		}

		// 创建ApiRules实例
		apiRule := &commons.ApiRules{
			UriPattern: api.UriPattern,
			Groups:     groups,
			Method:     api.Method,
		}

		// 将ApiRules实例添加到apiRules数组中
		ApiRules = append(ApiRules, apiRule)
	}
}

// 检查token并缓存会话信息
func checkTokenAndCacheSession(token string, context *gin.Context) bool {
	// 一个空的Session
	localSession := commons.Session{Groups: []int64{-1}}

	// token为空 设置游客组
	if token == "" {
		context.Set(commons.KEY_SESSION, localSession)
		return false
	}

	// 检查持久会话
	session, err := commons.GetSession(token)
	if err != nil {
		context.Set(commons.KEY_SESSION, localSession)
		return false
	}

	// 会话存在且未过期 ， 缓存会话到上下文
	context.Set(commons.KEY_SESSION, session)

	// 返回检查成功
	return true
}
